/*
 * Copyright 2009-2010 Nanjing RedOrange ltd (http://www.red-orange.cn)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package redora.generator;

import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import redora.util.DocPrint;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import static javax.xml.xpath.XPathConstants.NODESET;
import static redora.generator.XMLUtil.attributes;

/**
 * Fixes the caption elements on model and attributes. The default language captions are created from the model and
 * attribute names. And the optional table caption attributes are set from the form attribute.
 *
 * @author Nanjing RedOrange (http://www.red-orange.cn)
 */
public class Internationalizer {
    final String defaultLanguage;
    Document doc;

    /**
     * @param defaultLanguage (Mandatory) From maven pom settings or Redora default 'en'.
     */
    public Internationalizer(@NotNull String defaultLanguage) {
        this.defaultLanguage = defaultLanguage;
    }

    public void internationalize(@NotNull Document doc, @NotNull String modelName) throws ModelGenerationException {
        this.doc = doc;

        i18nObject(modelName);
        i18nAttributes();
        i18nEnum();
    }

    private void normalizeCaption(@NotNull Node caption) {
        XMLUtil.attribute(caption, "table", caption.getAttributes().getNamedItem("form").getNodeValue());
    }

    private void addCaption(Element parent, String captionSource) {
        Element tagElement = doc.createElement("caption");
        parent.appendChild(tagElement);
        XMLUtil.attribute(tagElement, "language", defaultLanguage);
        String captionName = StringUtils.join(
                StringUtils.splitByCharacterTypeCamelCase(
                        StringUtils.capitalize(captionSource)), ' ');

        XMLUtil.attribute(tagElement, "form", captionName);
        XMLUtil.attribute(tagElement, "table", captionName);
    }

    private void doElement(Element element, String nodeName) {
        boolean hasCaption = false;

        NodeList captions = element.getChildNodes();
        for (int i = 0; i < captions.getLength(); i++) {
            if ("caption".equals(captions.item(i).getNodeName())) {
                if (defaultLanguage.equals(captions.item(i).getAttributes().getNamedItem("language").getNodeValue()))
                    hasCaption = true;
                normalizeCaption(captions.item(i));
            }
        }

        if (!hasCaption) {
            addCaption(element, nodeName);
        }
    }

    private void i18nObject(String modelName) {
        doElement(doc.getDocumentElement(), modelName);
    }

    private void i18nAttributes() throws ModelGenerationException {
        NodeList attributes;
        try {
            attributes = attributes(doc, null);
        } catch (XPathExpressionException e) {
            DocPrint.print(doc);
            throw new ModelGenerationException("Failed to parse the attributes in this model", e);
        }
        for (int i = 0; i < attributes.getLength(); i++) {
            Node attribute = attributes.item(i);
            doElement((Element) attribute, attribute.getAttributes().getNamedItem("fieldName").getNodeValue());
        }
    }

    private void i18nEnum() throws ModelGenerationException {
        XPath xpath = XPathFactory.newInstance().newXPath();
        NodeList enums;
        try {
            enums = (NodeList)xpath.evaluate("//enum", doc, NODESET);
        } catch (XPathExpressionException e) {
            DocPrint.print(doc);
            throw new ModelGenerationException("Failed to parse the attributes in this model", e);
        }
        for (int i = 0; i < enums.getLength(); i++) {
            NodeList enumElements = enums.item(i).getChildNodes();

            for (int j = 0; j < enumElements.getLength(); j++) {
                Node element = enumElements.item(j);
                if ("element".equals(element.getNodeName()))
                    doElement((Element) element, element.getAttributes().getNamedItem("name").getNodeValue());
            }
        }
    }
}
